package mtn.lgdx.GPS;

public class Coord {
	private static final double a = 6378140, e1S = 0.00669438499959, e2S = 0.00673950181947; //80坐标系
	//private final double a = 6378245, e1S = 0.006693421622966, e2S = 0.006738525414683; //54坐标系
	private double rawLat, rawLon; //原生GCJ02 latitude & longitude
	private double lat, lon; // WGS84 latitude & longitude
	private int x, y; // Guass
	private double alfa, distance;

	public double getAlfa() {
		return alfa;
	}

	public double getDistance() {
		return distance;
	}
	
	public double getLat() {
		return Math.toDegrees(lat);
	}

	public double getLon() {
		return Math.toDegrees(lon);
	}
	
	public double getRawLat() {
		return Math.toDegrees(rawLat);
	}

	public double getRawLon() {
		return Math.toDegrees(rawLon);
	}
	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public Coord setCoord(double rawLat, double rawLon) {
		//gps84_to_gcj02(rawLat, rawLon);
		this.setRawLat(rawLat).setRawLon(rawLon);
		gcj02_to_gps84(rawLat, rawLon);
		return this;
	}
	
	public Coord setRawLat(double lat) {
		this.rawLat = Math.toRadians(lat);
		return this;
	}

	public Coord setRawLon(double lon) {
		this.rawLon = Math.toRadians(lon);
		return this;
	}
	
	public Coord setLat(double lat) {
		this.lat = Math.toRadians(lat);
		return this;
	}

	public Coord setLon(double lon) {
		this.lon = Math.toRadians(lon);
		return this;
	}

	public Coord setX(int x) {
		this.x = x;
		return this;
	}

	public Coord setY(int y) {
		this.y = y;
		return this;
	}
	
	private void gcj02_to_gps84(double lat, double lon) {
		transform(lat, lon);
		this.lat = Math.toRadians(lat + lat - this.lat);
		this.lon = Math.toRadians(lon + lon - this.lon);
	}
	
	private void gps84_to_gcj02(double lat, double lon) {
    	double dLat = transformLat(lon - 105.0,lat - 35.0);
    	double dLon = transformLon(lon - 105.0,lat - 35.0);
    	double radLat = Math.toRadians(lat);
    	double magic = Math.sin(radLat);
    	magic = 1 - e1S * magic * magic;
    	double sqrtMagic = Math.sqrt(magic);
    	dLat = (dLat * 180.0) / ((a * (1 - e1S)) / (magic * sqrtMagic) * Math.PI);
    	dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
    	this.rawLat = Math.toRadians(lat + dLat);
    	this.rawLon = Math.toRadians(lon + dLon);
	}
	
	private void transform(double lat, double lon) {
		double dLat = transformLat(lon - 105.0, lat - 35.0);
		double dLon = transformLon(lon - 105.0, lat - 35.0);
		double radLat = Math.toRadians(lat);
		double magic = Math.sin(radLat);
		magic = 1 - e1S * magic * magic;
		double sqrtMagic = Math.sqrt(magic);
		dLat = (dLat * 180.0) / ((a * (1 - e1S)) / (magic * sqrtMagic) * Math.PI);
		dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
		this.lat = lat + dLat;
		this.lon = lon + dLon;
	}
	
	private double transformLat(double x, double y) {
    	double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
    	ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
    	ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
    	ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
    	return ret;
	}

	private double transformLon(double x, double y) {
    	double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
    	ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
    	ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
    	ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
    	return ret;
	}
	
	/**逆运算**/
	public static void anticalc(Coord start, Coord end) {
		int dx, dy; //minus
		dx = end.x - start.x;
		dy = end.y - start.y;
		start.distance = Math.sqrt(dx * dx + dy * dy);
		if (start.distance == 0) {
			start.alfa = 0;
		}
		else {
			start.alfa = Math.acos(dx / start.distance); //radians
			if (dy < 0) { // >Math.PI
				start.alfa = Math.PI * 2 - start.alfa;
			}
		}
	}


	public Coord gauss2Geo() {
		// public static double[] GaussToBL(double X, double Y)//, double *longitude, double *latitude)
		double X = this.y, Y = this.x;
		int ProjNo; int ZoneWide; ////带宽 
		double longitude1,latitude1, longitude0, X0,Y0, xval,yval;//latitude0, 
		double e1,e2,f,a, ee, NN, T,C, M, D,R,u,fai;
	//	iPI = 0.0174532925199433; ////3.1415926535898/180.0; 
		//a = 6378245.0; f = 1.0/298.3; //54年北京坐标系参数 
		a = 6378140.0; f = 1.0 / 298.257; //80年西安坐标系参数
		ZoneWide = 6; ////6度带宽 
		ProjNo = (int)(X / 1000000L) ; //查找带号 
		longitude0 = Math.toRadians((ProjNo - 1) * ZoneWide + ZoneWide / 2); //中央经线
		X0 = ProjNo * 1000000L + 500000L;
		Y0 = 0; 
		xval = X - X0; 
		yval = Y - Y0; //带内大地坐标
		e2 = 2 * f - f * f;
		e1 = (1.0 - Math.sqrt(1 - e2)) / (1.0 + Math.sqrt(1 - e2));
		ee = e2 / (1 - e2);
		M = yval; 
		u = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256)); 
		fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * u) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * u) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * u) + (1097 * e1 * e1 * e1 * e1 / 512) * Math.sin(8 * u); 
		C = ee * Math.cos(fai) * Math.cos(fai); 
		T = Math.tan(fai) * Math.tan(fai); 
		NN = a / Math.sqrt(1.0 - e2 * Math.sin(fai) * Math.sin(fai)); 
		R = a * (1 - e2) / Math.sqrt((1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai))); D = xval / NN; 
		//计算经度(Longitude) 纬度(Latitude) 
		longitude1 = longitude0+(D-(1+2*T+C)*D*D*D/6+ (5-2*C+28*T-3*C*C+8*ee+24*T*T)*D *D*D*D*D/120)/Math.cos(fai); 
		latitude1 = fai -(NN*Math.tan(fai)/R)*(D*D/2-(5+3*T+10*C-4*C*C-9*ee)*D*D*D*D/24 +(61+90*T+298*C+45*T*T-256*ee-3*C*C)*D*D*D*D*D*D/720);
		this.lon = longitude1; 
		this.lat = latitude1; 
		gps84_to_gcj02(Math.toDegrees(latitude1), Math.toDegrees(longitude1));
		return this;
	}

	/**Geo to Gauss
	 ** param (B,L)
	 **/
	public Coord geo2Gauss() {
		double B = this.lat;
		double L = this.lon;
		double C1 = 1 + e1S / 4 * 3 + e1S * e1S / 64 * 45 + Math.pow(e1S, 3) / 256 * 175 + Math.pow(e1S, 4) / 16384 * 11025;
		double C2 = e1S / 4 * 3 + e1S * e1S / 16 * 15 + Math.pow(e1S, 3) / 512 * 525 + Math.pow(e1S, 4) / 2048 * 2205;
		double C3 = e1S * e1S / 64 * 15 + Math.pow(e1S, 3) / 256 * 105 + Math.pow(e1S, 4) / 4096 * 2205;
		double C4 = Math.pow(e1S, 3) / 512 * 35 + Math.pow(e1S, 4) / 2048 * 315;
		double C5 = Math.pow(e1S, 4) / 131072 * 312;
		double L0 = Math.toRadians(6) * Math.floor(L / Math.toRadians(6)) + Math.toRadians(3); //Central Lantitude below 1:25000 with 6 degree between each
		double deltaL = L - L0;
		double altaS = e2S * Math.cos(B) * Math.cos(B); //altaSquare
		double N = a / Math.sqrt(1 - e1S * Math.sin(B) * Math.sin(B));
		double X = a * (1 - e1S) * (C1 * B - C2 / 2 * Math.sin(2 * B) + C3 / 4 * Math.sin(4 * B) - C4 / 6 * Math.sin(6 * B) + C5 * Math.sin(8 * B));
		double t = Math.tan(B);

		x = (int)Math.round(X + N / 2 * Math.sin(B) * Math.cos(B) * deltaL * deltaL * (1 + deltaL * deltaL / 12 * Math.cos(B) * Math.cos(B) * (5 - t * t + 9 * altaS + 4 * altaS * altaS) + Math.pow(deltaL, 4) / 360 * Math.pow(Math.cos(B), 4) * (61 - 58 * t * t + Math.pow(t, 4))));
		y = (int)Math.ceil(L / Math.toRadians(6)) * 1000000 + 500000 + (int)Math.round(N * Math.cos(B) * deltaL * (1 + deltaL * deltaL / 6 * Math.cos(B) * Math.cos(B) * (1 - t * t + altaS) + Math.pow(deltaL, 4) / 120 * Math.pow(Math.cos(B), 4) * (5 - 18 * t * t + Math.pow(t, 4) + 14 * altaS - 58 * altaS * t * t)));
		return this;
	}

	/**弧度转密位**/
	public static int toMil(double alfa) {
		return (int)(alfa / Math.PI * 3000);
	}
}
